package com.gaiso.gaisopush;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.os.Process;
import android.support.annotation.Nullable;

import com.gaiso.gaisopush.data.PushConstant;
import com.gaiso.gaisopush.push.IPushCore;
import com.gaiso.gaisopush.push.PushCore;
import com.gaiso.gaisopush.utils.IDGenerator;
import com.gaiso.gaisopush.utils.LogUtil;


/**
 * Created by WangDongJie on 2017/4/20.
 */

public class PushService extends Service implements IPushCore.IPushCallback {

    private static final String ACTION_CONNECT = "action_connect";
    private static final String ACTION_DISCONNECT = "action_disconnect";
    private static final String ACTION_KEEP_ALIVE = "action_keep_alive";
    private static final String ACTION_RECONNECT = "action_reconnect";
    private static final String ACTION_SEND = "action_send";
    private static final String ACTION_SUBSCRIBE = "action_subscribe";

    private static final long KEEP_ALIVE_INTERVAL = 1000 * 60;
    private static final long INTERVAL_CONNECT_IF_START_FAIL = 1000 * 10;
    private IPushCore mPush;
    private boolean mStart = false;

    public static void actionStart(Context context) {
        Intent intent = new Intent(context.getApplicationContext(), PushService.class);
        intent.setAction(PushService.ACTION_CONNECT);
        context.getApplicationContext().startService(intent);
    }

    public static void actionStop(Context context) {
        Intent intent = new Intent(context.getApplicationContext(), PushService.class);
        intent.setAction( PushService.ACTION_DISCONNECT);
        context.getApplicationContext().startService(intent);
    }

    public static void actionSend() {

    }

    public static void actionSubscribe() {

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mPush = PushCore.getInstance();
        mPush.setCallback(this);
    }

    private void setStart(boolean flag) {
        mStart = flag;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        if (intent == null) {
            LogUtil.i("push service intent is null");
            actionStart(this);
            return START_STICKY;
        }
        String action = intent.getAction();
        action(action);
        return START_STICKY;
    }

    private BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);

            boolean hasConnectivity = (info != null && info.isConnected()) ? true : false;
            LogUtil.i("connect change: " + hasConnectivity);
            try {
                if (hasConnectivity) {
                    actionStart(PushService.this);
                } else {
                    setStart(false);
                }
            } catch (Exception e) {
                LogUtil.i("connect change exception " + e.toString());
            }
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
        LogUtil.i("push service is destroyed");
        Process.killProcess(Process.myPid());
    }

    private void keepAlive() {
        LogUtil.i("keep alive");
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_KEEP_ALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,
                KEEP_ALIVE_INTERVAL, pi);
    }

    private void cancelKeepAlive() {
        LogUtil.i("cancel keep alive");
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_KEEP_ALIVE);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.cancel(pi);
    }

    private synchronized void connect() {
        if (mStart) {
            LogUtil.i("the push has already connected");
            return;
        }
        LogUtil.i("connecting");
        String clientId = IDGenerator.getClientId(this);
        LogUtil.i("clientId: " + clientId);
        try {
            mPush.connect(clientId, PushConstant.BROKER);
            setStart(true);
            mPush.subscribe(clientId);
            keepAlive();
            registerConnectReceiver(true);
        } catch (Exception e) {
            LogUtil.i("connect fail " + e.toString());
            scheduleReconnect();
        }
    }

    private synchronized void disconnect() {
        if (!mStart) {
            LogUtil.i("the push has already disconnected");
            return;
        }
        try {
            LogUtil.i("disconnecting");
            mPush.disconnect();
            mPush.close();
            setStart(false);
            registerConnectReceiver(false);
            cancelReconnect();
            cancelKeepAlive();
        } catch (Exception e) {
            LogUtil.i("disconnect fail " + e.toString());
        }
    }

    private synchronized void sendKeepAlive() {
        try {
            LogUtil.i("send keep alive msg");
            mPush.keepAlive();
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.i("send keep alive exception " + e.toString());
        }
    }

    private void scheduleReconnect() {
        LogUtil.i("schedule reconnect");
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_CONNECT);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.set(AlarmManager.RTC_WAKEUP, INTERVAL_CONNECT_IF_START_FAIL, pi);
    }

    private void cancelReconnect() {
        LogUtil.i("cancel reconnect");
        Intent i = new Intent();
        i.setClass(this, PushService.class);
        i.setAction(ACTION_CONNECT);
        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
        AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
        alarmMgr.cancel(pi);
    }

    private void registerConnectReceiver(boolean register) {
        if (register) registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        else unregisterReceiver(mConnectivityChanged);
    }

    private void action(String action) {
        switch (action) {
            case ACTION_CONNECT:
                connect();
                break;
            case ACTION_DISCONNECT:
                disconnect();
                break;
            case ACTION_KEEP_ALIVE:
                sendKeepAlive();
                break;
            case ACTION_RECONNECT:
                try {
                    // TODO: 2017/4/21 reconnect
                } catch (Exception e) {
                    e.printStackTrace();
                    LogUtil.i("reconnect exception " + e.toString());
                }
                break;
            default:
                actionStart(this);
                break;
        }
    }

    @Override
    public void messageArrived(String topic, byte[] payload) {
        LogUtil.i("messageArrived: " + new String(payload));
        Intent intent = new Intent(PushConstant.PUSH_BROADCAST_RECEIVER_ACTION);
        intent.putExtra(PushConstant.BROADCAST_ACTION, PushConstant.GET_MSG_DATA);
        intent.putExtra(PushConstant.PAYLOAD, payload);
        sendBroadcast(intent);
    }
}
